package com.jic.weamop.weBankAMOP.common.server;

import com.jic.weamop.weBankAMOP.common.config.Config;
import com.jic.weamop.weBankAMOP.common.constants.BlockKeys;
import com.jic.weamop.weBankAMOP.common.constants.ResultEnum;
import com.jic.weamop.weBankAMOP.common.dto.response.ResponseData;
import org.bcos.web3j.crypto.Credentials;
import org.bcos.web3j.crypto.GenCredential;
import org.bcos.web3j.protocol.Web3j;
import org.bcos.web3j.protocol.channel.ChannelEthereumService;
import org.bcos.web3j.protocol.exceptions.TransactionTimeoutException;
import org.bcos.web3j.tx.Contract;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.math.BigInteger;


/**
 * 合约业务基类
 * 
 * @author v_wbgyang
 *
 */
@SuppressWarnings("all")
@Component
public  abstract class BaseService {
	private static final Logger logger = LoggerFactory.getLogger(BaseService.class);

	// 初始化签名
	protected static Credentials credentials;
	/*
	 * 普通链上链下消息的超时时间
	 */
	protected static Integer defaultDirectRouteRequestTimeout;
	/*
	 * 同步获取数据超时时间
	 */
	protected static Integer defaultFutureTimeout;

	protected Integer timeOut;

	protected static Config config;

	protected final static ContractServer server = Server.server;

	@Value("${defaultDirectRouteRequestTimeout}")
	private void setDefaultDirectRouteRequestTimeout(Integer defaultDirectRouteRequestTimeout){
		this.defaultDirectRouteRequestTimeout = defaultDirectRouteRequestTimeout;
	}

	@Value("${defaultFutureTimeout}")
	private void setDefaultFutureTimeout(Integer defaultFutureTimeout){
		this.defaultFutureTimeout = defaultFutureTimeout;
	}

	// 使用静态内部类 确保线程安全
	private static class Server {

		public final static ContractServer server = new ContractServer();

		static {
			server.loadConfig();
			// 初始化交易签名私钥

			credentials = GenCredential.create(config.getPrivKey());

			if (credentials == null) {
				logger.error(" credentials init fail");
				throw new RuntimeException("credentials init fail");
			} else {
				logger.info("credentials init succ");
			}
		}
	}

	/**
	 * 获取合约方法
	 * 
	 * @param contractAddress
	 * @param cls
	 * @param timeOut
	 * @return
	 */
	protected Contract getContract(String contractAddress, Class<?> cls, Integer timeOut) {
		try {
			logger.info("begin init " + cls.getSimpleName());
			// 获取合约load方法
			Method method = cls.getMethod("load", String.class, Web3j.class, Credentials.class, BigInteger.class,
					BigInteger.class);
			ChannelEthereumService channelEthereumService = this.getChannelEthereumService(timeOut);
			Web3j web3j = Web3j.build(channelEthereumService);
			Object contract = method.invoke(null, contractAddress, web3j, credentials, server.gasPrice,
					server.gasLimited);
			if (contract == null) {
				throw new RuntimeException(cls.getSimpleName() + " init fail");
			}
			logger.info(cls.getSimpleName() + " init succ");
			return (Contract) contract;
		} catch (Exception e) {
			throw new RuntimeException("加载合约对象失败:" + e.getMessage());
		}
	}

	/**
	 * 获取ChannelEthereumService 服务
	 * 
	 * @param timeOut
	 * @return
	 */
	private ChannelEthereumService getChannelEthereumService(Integer timeOut) {
		ChannelEthereumService channelEthereumService = new ChannelEthereumService();
		channelEthereumService.setChannelService(server.service);
		if (timeOut != null && timeOut.intValue() > 0) {
			// 设置超时时间
			channelEthereumService.setTimeout(timeOut.intValue());
		} else {
			// 如果没有自行设置时间 使用默认的超时时间
			channelEthereumService.setTimeout(defaultDirectRouteRequestTimeout);
		}
		return channelEthereumService;
	}

	/**
	 * 构建web3j服务
	 * 
	 * @param timeOut
	 * @return
	 */
	protected Web3j buildWeb3j(Integer timeOut) {
		return Web3j.build(this.getChannelEthereumService(timeOut));
	}

	/**
	 * 系统异常通用处理方式
	 * 
	 * @param response
	 * @param e
	 */
	protected void systemError(ResponseData<?> response, Throwable e) {
		if(e.getCause() != null && e.getCause() instanceof TransactionTimeoutException){
			systemTimeout(response, e);
			return;
		}
		response.setErrorCode(ResultEnum.SYSTEM_ERROR.getCode());
		response.setErrorMsg(ResultEnum.SYSTEM_ERROR.getCodeDesc() + ":" + e.getMessage());
		logger.error(BlockKeys.BLOCK_ERROR + response.getErrorCode() + ":" + response.getErrorMsg(), e);

	}

	/**
	 * 系统超时异常通用处理方式
	 * 
	 * @param response
	 * @param e
	 */
	protected void systemTimeout(ResponseData<?> response, Throwable e) {
		response.setErrorCode(ResultEnum.SYSTEM_TIME_OUT.getCode());
		response.setErrorMsg(ResultEnum.SYSTEM_TIME_OUT.getCodeDesc() + ":" + e.getMessage());
		logger.error(response.getErrorCode() + ":" + response.getErrorMsg(), e);
	}


}
